Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/项目笔记/代达罗斯/数据流向

Anatomy 的 DAO

3 分钟阅读 · Note

目录树 578 篇

            • 数据库迁移 Drizzle 迁移文件说明
            • Anatomy 的 DAO
            • Archetype的服务层
            • Archetype的DAO层
            • DAO&DTO
            • Zod Schema 模式
          • 项目待做
          • 性能优化
          • UI设计
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗数据库迁移 Drizzle 迁移文件说明同一路径↗Archetype的服务层同一路径↗Archetype的DAO层同一路径↗DAO&DTO同一路径↗Zod Schema 模式同一路径↗「Feature」Introduce Crate as an independent metadata entity[ˈentəti] | 引入 Crate 作为独立元数据实体共同主题
  • Anatomy 的 DAO

Anatomy 的 DAO

❯ createWithTx 与普通的create的区别是什么呢,为什么要这样去写呢
⎿ ⧉ Selected 1 lines from packages\db\src\dao\anatomies-dao.ts in Visual Studio Code

● Thinking for 6s… (ctrl+o to expand)

● 这是个非常关键的问题,理解了它才能真正会用事务。


create vs createWithTx 的区别

两者做的都是同一张表的 INSERT,唯一的区别是:它们在不在同一个事务里执行。

// 普通 create:直接拿全局 db 连接插入,自己就是一个独立事务 async create(input: AnatomyInsert): Promise<AnatomyRecord> { const rows = await db.insert(anatomiesTable).values(input).returning(); return rows[0]!; }

// createWithTx:用调用方传进来的 tx 插入,参与到调用方的事务里 async createWithTx( tx: DatabaseTransaction, input: AnatomyInsert, ): Promise<AnatomyRecord> { const rows = await tx.insert(anatomiesTable).values(input).returning(); return rows[0]!; }


为什么要这样写?

因为 Repository 需要把多张表的操作打包成一个原子操作。原子性的意思是:要么全部成功,要么全部回滚,不能出现“一半成功、一半失败”。

以 createDraft 为例,它必须同时做两件事:

  1. 在 anatomies 表插入一条身份记录
  2. 在 anatomy_drafts 表插入一条 Draft 记录

如果第一步成功了,第二步失败了,数据库里就会留下一个“没有 Draft 的 Anatomy”——这是脏数据。

所以 Repository 会这样写:

return db.transaction(async (tx) => { const anatomy = await anatomiesDao.createWithTx(tx, { id: crypto.randomUUID() }); const draft = await draftsDao.createWithTx(tx, { anatomyId: anatomy.id, ... }); return { anatomyId: anatomy.id, draftId: draft.id }; });

db.transaction(...) 开启一个事务,生成 tx。然后:

  • anatomiesDao.createWithTx(tx, ...) 用 tx 插入 anatomies
  • draftsDao.createWithTx(tx, ...) 用同一个 tx 插入 drafts

如果中途任何一步报错,Drizzle 会自动回滚整个事务,两张表都不会留下数据。


如果不用 createWithTx 会怎样?

假设你错误地混用了 db 和 tx:

return db.transaction(async (tx) => { const anatomy = await anatomiesDao.create({ id: crypto.randomUUID() }); // ❌ 用 db,不在事务里! const draft = await draftsDao.createWithTx(tx, { anatomyId: anatomy.id, ... }); return { anatomyId: anatomy.id, draftId: draft.id }; });

这时候:

  • anatomy 已经立刻提交到数据库了
  • draft 还在事务里
  • 如果 draft 插入失败回滚,anatomy 仍然留在表里

这就破坏了原子性。


项目里的固定模式

你去看 packages/db/src/dao/crates-dao.ts,它也有 create / createWithTx、update / updateWithTx、delete / deleteWithTx。这是项目里的标准写法:

  • DAO:同时提供普通版和 WithTx 版
  • Repository:需要跨表原子操作时,用 db.transaction(...) 开事务,然后把 tx 传给 DAO 的 WithTx 方法

所以记住一条规则:

▎ 在 db.transaction(...) 的回调里,所有数据库操作都必须用 tx,不能再用 db。


我接着把 repository/index.ts 的导出加上,然后跑类型检查。你还有什么想问的随时打断我。